#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <cstring>


#include "Log.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

static const uint16_t gport = 8888;
static const int glisten = -1;
static const int gbacklog = 8;

using task_t = std::function<void()>;

enum
{
    SOCK_ERROR = 1,
    BIND_ERROR,
    LISTEN_ERROR,
    ACEPT_ERROR
};

class TcpServer
{
public:
    TcpServer(uint16_t port = gport)
        : _port(port),
          _listenfd(glisten),
          _isrunning(false)
    {
    }

    void Init()
    {
        // 创建套接字
        _listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenfd < 0)
        {
            LOG(FATAL, "server socket creat error\n");
            exit(SOCK_ERROR);
        }
        LOG(DEBUG, "server creat success, lsistenfd: %d\n", _listenfd);

        // bind
        struct sockaddr_in local;
        socklen_t len = sizeof(local);
        memset(&local, 0, len);

        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = ::bind(_listenfd, (struct sockaddr *)&local, len);
        if (n < 0)
        {
            LOG(FATAL, "server bind error\n");
            exit(BIND_ERROR);
        }
        LOG(DEBUG, "server bind success\n");

        // 监听
        n = ::listen(_listenfd, gbacklog);
        if (n < 0)
        {
            LOG(FATAL, "server listen error\n");
            exit(LISTEN_ERROR);
        }
        LOG(DEBUG, "server listen success\n");
    }
    class ThreadData
    {
    public:
        TcpServer* _self;
        int _sockfd;
        InetAddr _addr;
        ThreadData(int& sockfd,TcpServer* self,InetAddr& addr)
        :_sockfd(sockfd),_self(self),_addr(addr)
        {}
    };
    void Loop()
    {
        //子进程不会变成僵尸进程；子进程资源仍然会被保留；父进程想要释放子进程资源,
                                // 需要调用wait/waitpid,否则大量被终止的子进程被记录，可能导致系统性能下降或资源耗尽
        //非阻塞轮询等待: signal(SIGCHLD,handler);//忽略不是阻塞，忽略是信号递达后的一种处理行为;
        
        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            // accept 接收
            int sockfd = accept(_listenfd, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                LOG(WARNING, "server accept error\n");
                continue;
            }

            InetAddr addr(client);
            LOG(INFO, "get a new link, client info : %s\n", addr.AddrStr().c_str());
            /*
                //v1 多进程
                pid_t id = fork();
                if(id == 0)
                {
                //父子进程会有同一份文件描述符表（引用计数，不是共享，写实拷贝），指向同一个文件，子进程只关心已链接描述符，父进程只需要关心监听描述符
                //防止文件描述符泄漏 
                ::close(_listenfd);

                    //创建子进程，子进程销毁，父进程阻塞等待，回收子进程;
                    //创建孙子进程 释放子进程，孙子进程编程孤儿进程，由os管理，提供task_structs
                    if(fork() > 0)
                    {
                        //子进程立即返回
                        exit(0);
                    }
                    
                    Service(sockfd,addr);
                    exit(0);

                }
                ::close(sockfd);
                int n = waitpid(id,nullptr,0);  //WNOHANG 非阻塞等待
                if(n > 0)
                {
                LOG(INFO, "wait child success.\n");   
                }
            */
            /*
                //v2 多线程
                ThreadData* td = new ThreadData(sockfd,this,addr);
                pthread_t tid;
                pthread_create(&tid,NULL,Excute,td); 
            */
            //v3 线程池 
            task_t t = std::bind(&TcpServer::Service,this,sockfd,addr);
            ThreadPool<task_t>::GetInstance()->Equeue(t);        
            //Service(sockfd, addr);
        }
        _isrunning = false;
    }

    static void* Excute(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* ptd = static_cast<ThreadData*>(args);
        ptd->_self->Service(ptd->_sockfd,ptd->_addr);
        delete ptd;
        return nullptr;
    }

    void Service(int sockfd, InetAddr addr)
    {
        while (true)
        {
            char inbuff[1024];
            int n = ::read(sockfd, inbuff, sizeof(inbuff) - 1);
            if (n > 0)
            {
                inbuff[n] = 0;
                std::string message = "[server echo]# ";
                message += inbuff;
                std::cout << '[' << addr.AddrStr()<<"]# "<<inbuff<<std::endl;
                
                ::write(sockfd,message.c_str(),message.size());
            }
            else if (n == 0)
            {
                LOG(INFO, "[%s] quit\n",addr.AddrStr().c_str());
                break;
            }
            else
            {
                LOG(ERROR, "server read %s error\n",addr.AddrStr().c_str());
                break;
            }
        }
    }

    ~TcpServer()
    {
        if (_listenfd > 0)
            ::close(_listenfd);
    }

private:
    uint16_t _port;
    int _listenfd;
    bool _isrunning;
};